package com.swang.security.sdk;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.swang.security.sdk.integration.SdkAuthorityClient;
import com.swang.security.sdk.integration.SdkSecurityClient;

/**
 * @ClassName: UserDetailsServiceImpl
 * @Description: 用户授权用户详情服务
 * @author swang
 * @date 2017年4月16日 下午1:12:59
 */
public class SwangUserDetailsService implements UserDetailsService, InitializingBean {
	private Log logger = LogFactory.getLog(SwangUserDetailsService.class);
	
	private SdkSecurityClient sdkSecurityClient;
	
	private SdkAuthorityClient sdkAuthorityClient;
	
	private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
	
	private Map<String, CustomUser> customUserMap;
	
	protected List<SwangGrantedAuthority> customAuthorities = Arrays.asList(new SwangGrantedAuthority("ROLE_USER"));
	
	/** 引用服务名，默认为common */
	private String appAuthName = Config.localAppAuthName;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
		Validate.notEmpty(username);
		User user = sdkSecurityClient.findUserByUserName(username);
		CustomUser customUser = customUserMap!=null?customUserMap.get(username):null;
		SwangUserDetails userDetails;
		Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();
		if (user==null || user.getUserId()==null || user.getUserId()<=0 || StringUtils.isBlank(user.getUsername())) {
			String logMsg = "查询用户'" + username + "'返回结果为空。"+JSON.toJSONString(user);
			String exceptionMsg = messages.getMessage("findUserByUserName({0})", new Object[] { username },"用户:"+username+"不存在！"+JSON.toJSONString(user));
			if(customUser==null || StringUtils.isBlank(customUser.getUsername())){
				logger.debug(logMsg);
				throw new UsernameNotFoundException(exceptionMsg);
			} else {
				logMsg = logMsg+"将尝试使用自定义用户登录认证";
				logger.warn(logMsg, new UsernameNotFoundException(exceptionMsg));
				userDetails = customUser;
				userDetails.setAccountNonExpired(true);
				userDetails.setCredentialsNonExpired(true);
				dbAuthsSet.addAll(customUser.getAuthorities());
			}
		}else{
			userDetails = this.createUserDetails(user);
			if(customUser!=null && !StringUtils.isBlank(customUser.getUsername())){
				dbAuthsSet.addAll(customUser.getAuthorities());
			}
		}
		dbAuthsSet.addAll(customAuthorities);
		
		List<GrantedAuthority> grantedAuthorities = sdkAuthorityClient.findAuthoritiesByUserId(user.getUserId());
		dbAuthsSet.addAll(grantedAuthorities);
		
		List<GrantedAuthority> dbAuthList = new ArrayList<GrantedAuthority>(dbAuthsSet);
		if (dbAuthList.size() == 0) {
			logger.debug("用户'" + username + "'没有权限资源，该用户将被当做‘用户不存在’处理");
			throw new UsernameNotFoundException(messages.getMessage("SwangUserDetailsService.noAuthority", new Object[] { username }, "用户"+username+"没有被授予权限资源"));
		}
		userDetails.setAuthorities(dbAuthList);
		userDetails.setAppAuthName(appAuthName);
		return userDetails;
	}
	
	protected SwangUserDetails createUserDetails(User user) {
		SwangUserDetails ud = new SwangUserDetails();
		BeanUtils.copyProperties(user, ud);
		ud.setAccountNonExpired(true);
		ud.setCredentialsNonExpired(true);
		return ud;
	}
	
	public void setCustomAuthoritiesAsString(String customAuthoritiesAsString) {
		if (StringUtils.isBlank(customAuthoritiesAsString)) {
			customAuthorities = Collections.emptyList();
		} else {
			customAuthoritiesAsString = customAuthoritiesAsString.trim();
			Validate.isTrue(customAuthoritiesAsString.matches("^\\s*ROLE_[a-zA-Z0-9-_]+([,| |\t]+ROLE_[a-zA-Z0-9-_]+)*\\s*$"));
			String[] auths = customAuthoritiesAsString.split("[,| |\t]+");
			customAuthorities = new ArrayList<SwangGrantedAuthority>();
			for (String auth : auths) {
				customAuthorities.add(new SwangGrantedAuthority(auth.trim()));
			}
		}
	}
	
	public void setCustomAuthorities(List<SwangGrantedAuthority> customAuthorities) {
		this.customAuthorities = customAuthorities;
	}

	public void setSdkSecurityClient(SdkSecurityClient sdkSecurityClient) {
		this.sdkSecurityClient = sdkSecurityClient;
	}

	public void setSdkAuthorityClient(SdkAuthorityClient sdkAuthorityClient) {
		this.sdkAuthorityClient = sdkAuthorityClient;
	}

	public void setCustomUserMap(Map<String, CustomUser> customUserMap) {
		this.customUserMap = customUserMap;
	}
	
	public void setCustomUsers(List<CustomUser> customUsers) {
		if(CollectionUtils.isEmpty(customUsers)){
			return;
		}
		this.customUserMap = new HashMap<String, CustomUser>();
		for (CustomUser user : customUsers) {
			if(!StringUtils.isBlank(user.getUsername())){
				customUserMap.put(user.getUsername().trim(), user);
			}
		}
	}

	public void setAppAuthName(String appAuthName) {
		if(StringUtils.isBlank(appAuthName)){
			return;
		}
		String config = "#{resourceServerConfig.getAppAuthName()}";
		if(config.equals(appAuthName.trim())){
			return;
		}
		this.appAuthName = appAuthName;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(this.sdkSecurityClient, "SdkSecurityClient注入不能为空");
		Assert.notNull(this.sdkAuthorityClient, "SdkAuthorityClient注入不能为空");
	}

}
